'''
Created on 18/09/2011

@author: Almada, Correa, Ventura
'''

from string import find

#diccionario de variables
memoria = {}
#diccionario de tipos
variablesTipos = {}
BOOL = 'B'
INT = 'I'
NUM = 'N'
STRING = 'S'

def imprimirParen(precedencia, valor):
    resultado = ''
    if (valor.precedencia > precedencia):
        resultado += '(' + str(valor.mostrar()) + ')'
    else:
        resultado += str(valor.mostrar())
    return resultado

def imprimirParenDosValores(simbolo, precedencia, valor1, valor2):
    resultado = ''
    if (valor1.precedencia >= precedencia):
        resultado +=  '(' + str(valor1.mostrar()) + ')'
    else:
        resultado += str(valor1.mostrar())
    resultado += simbolo
    if (valor2.precedencia >= precedencia):
        resultado +=  '(' + str(valor2.mostrar()) + ')'
    else:
        resultado +=  str(valor2.mostrar())
    return resultado  

def convertirBool(valor):
    if valor:
        return "true"
    else:
        return "false"

def esFloat(valor):
    if (find(valor, '.') > 0):
        try:
            #Pruebo a castear el valor para corroborar que efectivamente es un float
            pruebaCast = float(valor)
            return True
        except ValueError:
            return False
    else:
        return False
    
def esInt(valor):
    if (not esFloat(valor)):
        try:
            #Pruebo a castear el valor para corroborar que efectivamente es un int
            pruebaCast = int(valor)
            return True
        except ValueError:
            return False
    else:
        return False
        
    
#def esFloat(valor):
#    if (find(valor, '.') > 0):
#        return True
#    else:
#        return False

class Sentencia():
    def __init__(self, p1, tieneParen):
        self.valor1 = p1
        self.tieneParen = tieneParen
        self.precedencia = 0
    
    def check(self):
        return self.valor1.check()
                
    def operar(self):
        return self.valor1.operar()
    
    def mostrar(self):
        if self.tieneParen:
            return imprimirParen(0, self.valor1)
        else:
            return self.valor1.mostrar() 
    
class Secuencia():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 0
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()    
    
    def operar(self):
        self.valor1.operar()
        return self.valor2.operar()    
            
    def mostrar(self):
        resultado = str(self.valor1.mostrar())
        resultado += '; \n' + str(self.valor2.mostrar())
        return resultado
        
class Bloque():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 0
    
    def check(self):
        return self.valor1.check()
        
    def operar(self):
        return self.valor1.operar()
    
    def mostrar(self):
        resultado = '{' + '\n'
        resultado += self.valor1.mostrar() + '\n' + '}'
        return resultado
            
class Iteracion():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 0

    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()    
            
    def operar(self):
        if (self.valor1.operar() == "true"):
            a = self.valor2.operar()
            while (self.valor1.operar() == "true"):
                a = self.valor2.operar()
            return a
        
    def mostrar(self):
        resultado = 'while ' + self.valor1.mostrar() + '\n' 
        resultado += 'repeat' + '\n'
        resultado += self.valor2.mostrar()
        return resultado
    
class IteracionElse():
    def __init__(self, p1, p2, p3):
        self.valor1 = p1
        self.valor2 = p2
        self.valor3 = p3
        self.precedencia = 0
        
    def check(self):
        if self.valor1.check() == 'OK':
            if self.valor2.check() == 'OK':
                return self.valor3.check()
            else:
                return self.valor2.check()
        else:
            return self.valor1.check()
            
    def operar(self):
        if (self.valor1.operar() == "true"):
            a = self.valor2.operar()
            while (self.valor1.operar() == "true"):
                a = self.valor2.operar()
            return a
        else:
            return self.valor3.operar()
    
    def mostrar(self):
        resultado = 'while ' + self.valor1.mostrar() + '\n' 
        resultado += 'repeat' + '\n'
        resultado += self.valor2.mostrar() + '\n'
        resultado += 'else' + '\n'
        resultado += self.valor3.mostrar()
        return resultado
        
class Condicional():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 0

    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()    
    
    def operar(self):
        if (self.valor1.operar() == "true"):
            return self.valor2.operar()
        else:
            return None
        
    def mostrar(self):
        resultado = 'if ' + self.valor1.mostrar() + ' then' + '\n'
        resultado += self.valor2.mostrar()
        return resultado 
        
class CondicionalElse():
    def __init__(self, p1, p2, p3):
        self.valor1 = p1
        self.valor2 = p2
        self.valor3 = p3
        self.precedencia = 0

    def check(self):
        if self.valor1.check() == 'OK':
            if self.valor2.check() == 'OK':
                return self.valor3.check()
            else:
                return self.valor2.check()
        else:
            return self.valor1.check()

    def operar(self):
        if (self.valor1.operar() == "true"):
            return self.valor2.operar()
        else:
            return self.valor3.operar()
        
    def mostrar(self):
        resultado = 'if ' + self.valor1.mostrar() + ' then ' + '\n'
        resultado += self.valor2.mostrar() + '\n'
        resultado += 'else' + '\n'
        resultado += self.valor3.mostrar()
        return resultado 
        

class DeclaracionBool():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 0
    
    def check(self):
        if (variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " ya declarada!"
        else:
            variablesTipos[self.valor1] = BOOL
            return "OK"       
        
    def operar(self):
        memoria[self.valor1] = None
        
    def ObtenerValorDefecto(self):
        return "false"
    
    def mostrar(self):
        return "bool %s" %(self.valor1) 

class DeclaracionInt():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 0
    
    def check(self):
        if (variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " ya declarada!"
        else:
            variablesTipos[self.valor1] = INT
            return "OK"
        
    def operar(self):
        memoria[self.valor1] = None

    def ObtenerValorDefecto(self):
        return 0
    
    def mostrar(self):
        return "int %s" %(self.valor1) 

class DeclaracionNum():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 0
    
    def check(self):
        if (variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " ya declarada!"
        else:
            variablesTipos[self.valor1] = NUM
            return "OK"
        
    def operar(self):
        memoria[self.valor1] = None
    
    def ObtenerValorDefecto(self):
        return 0
    
    def mostrar(self):
        return "num %s" %(self.valor1) 

class DeclaracionString():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 0
    
    def check(self):
        if (variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " ya declarada!"
        else:
            variablesTipos[self.valor1] = STRING
            return "OK"
        
    def operar(self):
        memoria[self.valor1] = None

    def ObtenerValorDefecto(self):
        return ""
        
    def mostrar(self):
        return "string %s" %(self.valor1)  
    
class AsignacionBool():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 0
    
    def check(self):
        if (variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " ya declarada!"
        else:
            variablesTipos[self.valor1] = BOOL
            return self.valor2.check()
        
    def operar(self):
        memoria[self.valor1] = self.valor2.operar()
        
    def ObtenerValorDefecto(self):
        return "false"
    
    def mostrar(self):
        return 'bool %s := %s' %(self.valor1, str(self.valor2.mostrar()))

class AsignacionInt():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 0
    
    def check(self):
        if (variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " ya declarada!"
        else:
            variablesTipos[self.valor1] = INT
            return self.valor2.check()
        
    def operar(self):
        memoria[self.valor1] = self.valor2.operar()

    def ObtenerValorDefecto(self):
        return 0
    
    def mostrar(self):
        return 'int %s := %s' %(self.valor1, str(self.valor2.mostrar()))

class AsignacionNum():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 0
    
    def check(self):
        if (variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " ya declarada!"
        else:
            variablesTipos[self.valor1] = NUM
            return self.valor2.check()
        
    def operar(self):
        memoria[self.valor1] = self.valor2.operar()
    
    def ObtenerValorDefecto(self):
        return 0
    
    def mostrar(self):
        return 'num %s := %s' %(self.valor1, str(self.valor2.mostrar()))

class AsignacionString():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 0
    
    def check(self):
        if (variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " ya declarada!"
        else:
            variablesTipos[self.valor1] = STRING
            return self.valor2.check()
        
    def operar(self):
        memoria[self.valor1] = str(self.valor2.operar())

    def ObtenerValorDefecto(self):
        return ""
        
    def mostrar(self):
        return 'string %s := %s' %(self.valor1, str(self.valor2.mostrar()))
 
class Asignacion():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 0
    
    def check(self):
        if (not variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " aun no declarada!"
        else:
            #Aca deberiamos hacer checkeo de tipos
            return self.valor2.check()
        
    def operar(self):
        memoria[self.valor1] = self.valor2.operar()
        
    def mostrar(self):
        variable = self.valor1
        valor = str(self.valor2.mostrar())
        return '%s := %s' % (variable, valor)

#class Expresion():
#    def __init__(self, p1):
#        self.valor1 = p1
#        self.procedencia = 0
#        
#    def operar(self):
#        return self.valor1.operar()
#    
#    def mostrar(self):
#        imprimirParen(self.procedencia, self.valor1)

class Valor():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 0
        
    def check(self):
        return "OK"
        
    def operar(self):
        if (esFloat(self.valor1)):
            return float(self.valor1)
        else:
            return int(self.valor1)
    
    def mostrar(self):
        if (esFloat(self.valor1)):
            return '%f' %(float(self.valor1))
        else:
            return '%d' %(int(self.valor1))
    
class ValorString():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 0
        
    def check(self):
        return "OK"
        
    def operar(self):
        return self.valor1
 
    def mostrar(self):
        return "'" + self.valor1 + "'"
 
class Variable():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 0
    
    def check(self):
        if (not variablesTipos.has_key(self.valor1)):
            return "Error de compilacion: Variable " + self.valor1 + " no declarada!"
        else:
            return "OK"
        
    def operar(self):
        return memoria[self.valor1]
        
    def mostrar(self):
        return self.valor1

# IGUAL
class Igual():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1

    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()

    def operar(self):
        return convertirBool(self.valor1.operar() == self.valor2.operar())
    
    def mostrar(self):
        return imprimirParenDosValores(' = ', self.precedencia, self.valor1, self.valor2)

# DIFERENTE
class Diferente():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1

    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()

    def operar(self):
        return convertirBool(self.valor1.operar() <> self.valor2.operar())

    def mostrar(self):
        return imprimirParenDosValores(' <> ', self.precedencia, self.valor1, self.valor2)
    
    
# MENOR    
class Menor():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()
        
    def operar(self):
        return convertirBool(self.valor1.operar() < self.valor2.operar())

    def mostrar(self):
        return imprimirParenDosValores(' < ', self.precedencia, self.valor1, self.valor2)

# MAYOR
class Mayor():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        return convertirBool(self.valor1.operar() > self.valor2.operar())
    
    def mostrar(self):
        return imprimirParenDosValores(' > ', self.precedencia, self.valor1, self.valor2)

# MENORIGUAL
class MenorIgual():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        return convertirBool(self.valor1.operar() <= self.valor2.operar())

    def mostrar(self):
        return imprimirParenDosValores(' <= ', self.precedencia, self.valor1, self.valor2)

# MAYORIGUAL
class MayorIgual():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        return convertirBool(self.valor1.operar() >= self.valor2.operar())

    def mostrar(self):
        return imprimirParenDosValores(' >= ', self.precedencia, self.valor1, self.valor2)
    
# NOT
class Not():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 1
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        if (self.valor1.operar() == "true"):
            return "false"
        else:
            return "true"

    def mostrar(self):
        resultado = '!'
        resultado +=  imprimirParen(self.precedencia, self.valor1)
        return resultado

# AND
class And():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 2
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        if (self.valor1.operar() == "true") and (self.valor2.operar() == "true"):
            return "true"
        else:
            return "false"
    
    def mostrar(self):
        return imprimirParenDosValores(' & ', self.precedencia, self.valor1, self.valor2)

# OR
class Or():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 3
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        if (self.valor1.operar() == "true") or (self.valor2.operar() == "true"):
            return "true"
        else:
            return "false"

    def mostrar(self):
        return imprimirParenDosValores(' | ', self.precedencia, self.valor1, self.valor2)

# SUMA
class Suma():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 2
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        resultado = self.valor1.operar() + self.valor2.operar()
        return resultado

    def mostrar(self):
        return imprimirParenDosValores(' + ', self.precedencia, self.valor1, self.valor2)

# RESTA
class Resta():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 2
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        resultado = self.valor1.operar() - self.valor2.operar()
        return resultado
    
    def mostrar(self):
        return imprimirParenDosValores(' - ', self.precedencia, self.valor1, self.valor2)

# MULTIPLICACION
class Multiplicacion():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        resultado = self.valor1.operar() * self.valor2.operar()
        return resultado

    def mostrar(self):
        return imprimirParenDosValores(' * ', self.precedencia, self.valor1, self.valor2)

# DIVISION
class Division():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        resultado = self.valor1.operar() / self.valor2.operar()
        return resultado

    def mostrar(self):
        return imprimirParenDosValores(' / ', self.precedencia, self.valor1, self.valor2)

# MODULO
class Modulo():
    def __init__(self, p1, p2):
        self.valor1 = p1
        self.valor2 = p2
        self.precedencia = 1
        
    def check(self):
        if self.valor1.check() == 'OK':
            return self.valor2.check()
        else:
            return self.valor1.check()        
        
    def operar(self):
        resultado = self.valor1.operar() % self.valor2.operar()
        return resultado

    def mostrar(self):
        return imprimirParenDosValores(' % ', self.precedencia, self.valor1, self.valor2)
   
#Clases de casting
#Segun fue consultado a los profesores Asumimos que solo se castean valores, 
#No se castean expresiones ni variables ya que la nomenclatura es ambigua.

class CastInt():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 1
        
    def check(self):
        return self.valor1.check()        
        
    def operar(self):
        if (esInt(self.valor1.operar())):
            return int(self.valor1.operar())
        else:
            valorFloat = float(self.valor1.operar())
            return int(valorFloat)

    def mostrar(self):
        resultado = 'int '
        if ((not esFloat(self.valor1.operar())) and (not esInt(self.valor1.operar()))):
            resultado += "'" + self.valor1.mostrar() + "'"
        else:
            resultado += self.valor1.mostrar()
        return resultado

class CastTexto():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 1

    def check(self):
        return self.valor1.check()
        
    def operar(self):
        return str(self.valor1.operar())

    def mostrar(self):
        resultado = 'str ' + self.valor1.mostrar()
        return resultado

class CastNum():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 1
        
    def check(self):
        return self.valor1.check()        
        
    def operar(self):
        return float(self.valor1.operar())
    
    def mostrar(self):
        resultado = 'num '
        if ((not esFloat(self.valor1.operar())) and (not esInt(self.valor1.operar()))):
            resultado += "'" + self.valor1.mostrar() + "'"
        else:
            resultado += self.valor1.mostrar()
        return resultado

class CastBool():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 1
        
    def check(self):
        return self.valor1.check()        
        
    def operar(self):
        if (self.valor1.operar() == "true"):
            return "true"
        else:
            return "false"

    def mostrar(self):
        resultado = 'bool '
        resultado += "'" + self.valor1.mostrar() + "'"
        return resultado

class Length():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 1
        
    def check(self):
        return self.valor1.check()        
        
    def operar(self):
        return len(self.valor1.operar())

    def mostrar(self):
        resultado = 'length '
        resultado += imprimirParen(self.precedencia, self.valor1)
        return resultado

class Write():
    def __init__(self, p1):
        self.valor1 = p1
        self.precedencia = 1
        
    def check(self):
        return self.valor1.check()        
        
    def operar(self):
        print (self.valor1.operar())
        return (self.valor1.operar())
    
    def mostrar(self):
        resultado = 'write '
        resultado += imprimirParen(self.precedencia, self.valor1)
        return resultado
        
class Read():
    def __init__(self):
        self.precedencia = 1
        
    def check(self):
        return "OK"       
        
    def operar(self):
        resultado = raw_input()
        return resultado
    
    def mostrar(self):
        return 'read'
    